/*
 * author: VDaras
 */

#include "Surface.h"
#include "../Mathematics.h"
#include "Color.h"
#include <cstring>
#include <SDL/SDL_opengl.h>

namespace sdl
{

Surface::Surface()
{
    m_buffer = NULL;
}

Surface::~Surface()
{
    FreeBuffer();
}

GLuint Surface::ConvertToTexture(GLint minFilter,GLint magFilter)
{
    // Check that the image's width is a power of 2
    if (!(Math::IsPowerOf2(m_buffer->w) && Math::IsPowerOf2(m_buffer->h)))
    {
        //return 0;
    }
    
    GLuint textureID;
    glGenTextures(1,&textureID);

    GLenum textureFormat;
    // get the number of channels in the SDL surface
    GLuint nOfColors = m_buffer->format->BytesPerPixel;
    if (nOfColors == 4) // contains an alpha channel
    {
        if (m_buffer->format->Rmask == 0x000000ff)
            textureFormat = GL_RGBA;
        else
            textureFormat = GL_BGRA;
    }
    else if (nOfColors == 3) // no alpha channel
    {
        if (m_buffer->format->Rmask == 0x000000ff)
            textureFormat = GL_RGB;
        else
            textureFormat = GL_BGR;
    }
    else
    {
        return 0;
    }

    // Bind the texture object
    glBindTexture(GL_TEXTURE_2D, textureID);

    // Set the texture's stretching properties
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

    // Edit the texture object's image data using the information SDL_Surface gives us
    glTexImage2D(GL_TEXTURE_2D, 0, nOfColors, m_buffer->w, m_buffer->h, 0,
                 textureFormat, GL_UNSIGNED_BYTE, m_buffer->pixels);
    return textureID;
}

/*
 * Returns whether m_buffer points to a valid SDL_Surface.
 */

bool Surface::Valid() const
{
    return m_buffer != NULL;
}

/*
 * Returns the width of the surface
 */

int Surface::GetWidth() const
{
    if (m_buffer)
        return m_buffer->w;

    return 0;
}

/*
 * Returns the height of the surface
 */

int Surface::GetHeight() const
{
    if (m_buffer)
        return m_buffer->h;

    return 0;
}

/*
 * Sets the desired pixel to the desired color.
 *
 * @param
 * - x,y : pixel's coordiantes
 * - color: desired color
 */

void Surface::SetPixel(int x, int y, const Color& color)
{
    //if the buffer is valid
    if (m_buffer)
    {
        //if the point x,y doesn't is outside the surface, return
        if (x < 0 || x >= GetWidth() || y < 0 || y >= GetHeight())
            return;

        //create a mapped color
        Uint32 mappedColor = SDL_MapRGB(m_buffer->format, color.GetR(), color.GetG(), color.GetB());

        //get pixel data
        char *pData = (char *) m_buffer->pixels;
        //apply desired pitch
        pData += (y * m_buffer->pitch);
        //apply desired offset
        pData += (x * m_buffer->format->BytesPerPixel);
        //copy bytes to the buffer
        memcpy(pData, &mappedColor, m_buffer->format->BytesPerPixel);
    }
}

/*
 * This routine fills a portion of the surface with the desired color
 *
 * @param
 * - x,y : upper left area coordinate
 * - w: area width
 * - h: area height
 */

void Surface::FillArea(int x, int y, int w, int h, const Color& color)
{


    Uint32 colorkey = SDL_MapRGB(m_buffer->format, color.GetR(), color.GetG(), color.GetB());
    SDL_Rect rect = {x, y, w, h};

    SDL_FillRect(m_buffer, &rect, colorkey);
}

/*
 * Returns primitive SDL_Surface
 */

SDL_Surface *Surface::GetBuffer() const
{
    return m_buffer;
}

/*
 * Set's the m_buffer to point to another surface. If m_buffer doesn't point to
 * NULL, free's surface first.
 *
 * @param
 * - buffer: the new buffer
 */

void Surface::SetBuffer(SDL_Surface* buffer)
{
    FreeBuffer();

    m_buffer = buffer;
}

/*
 * Free's m_buffer
 */

void Surface::FreeBuffer()
{
    if (m_buffer)
    {
        SDL_FreeSurface(m_buffer);
        m_buffer = NULL;
    }
}

};

